package cn.iocoder.yudao.module.datasource.service.config;

import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.datasource.controller.admin.config.vo.DataSourceConfigPageReqVO;
import cn.iocoder.yudao.module.datasource.controller.admin.config.vo.DataSourceConfigSaveReqVO;
import cn.iocoder.yudao.module.datasource.core.DynamicRoutingDataSource;
import cn.iocoder.yudao.module.datasource.dal.dataobject.config.DynamicDataSourceConfigDO;
import cn.iocoder.yudao.module.datasource.dal.mysql.config.DataSourceConfigMapper;
import cn.iocoder.yudao.module.datasource.enums.DataSourceTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.List;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.datasource.enums.ErrorCodeConstants.*;

/**
 * 数据源配置 Service 实现类
 *
 * @author 芋道源码
 */
@Service("dynamicDataSourceConfigService")
@Validated
@Slf4j
public class DynamicDataSourceConfigServiceImpl implements DataSourceConfigService {

    @Resource
    @Qualifier("dataSourceConfigMapper")
    private DataSourceConfigMapper dynamicDataSourceConfigMapper;

    @Autowired(required = false)
    private DynamicRoutingDataSource dynamicRoutingDataSource;

    @Override
    public Long createDataSourceConfig(DataSourceConfigSaveReqVO createReqVO) {
        // 校验名称唯一性
        validateNameUnique(null, createReqVO.getName());
        // 校验数据库类型
        validateDatabaseType(createReqVO.getType());

        // 插入
        DynamicDataSourceConfigDO dataSourceConfig = BeanUtils.toBean(createReqVO, DynamicDataSourceConfigDO.class);
        dynamicDataSourceConfigMapper.insert(dataSourceConfig);
        
        // 返回
        return dataSourceConfig.getId();
    }

    @Override
    public void updateDataSourceConfig(DataSourceConfigSaveReqVO updateReqVO) {
        // 校验存在
        validateDataSourceConfigExists(updateReqVO.getId());
        // 校验名称唯一性
        validateNameUnique(updateReqVO.getId(), updateReqVO.getName());
        // 校验数据库类型
        validateDatabaseType(updateReqVO.getType());

        // 更新
        DynamicDataSourceConfigDO updateObj = BeanUtils.toBean(updateReqVO, DynamicDataSourceConfigDO.class);
        dynamicDataSourceConfigMapper.updateById(updateObj);

        // 如果数据源已经创建，需要删除重建
        if (dynamicRoutingDataSource != null) {
            String dataSourceKey = String.valueOf(updateReqVO.getId());
            if (dynamicRoutingDataSource.existsDataSource(dataSourceKey)) {
                dynamicRoutingDataSource.delDatasource(dataSourceKey);
                log.info("数据源配置更新，删除旧数据源: {}", dataSourceKey);
            }
        }
    }

    @Override
    public void deleteDataSourceConfig(Long id) {
        // 校验存在
        validateDataSourceConfigExists(id);

        // 删除动态数据源
        if (dynamicRoutingDataSource != null) {
            String dataSourceKey = String.valueOf(id);
            if (dynamicRoutingDataSource.existsDataSource(dataSourceKey)) {
                dynamicRoutingDataSource.delDatasource(dataSourceKey);
                log.info("删除数据源配置，同步删除动态数据源: {}", dataSourceKey);
            }
        }

        // 删除
        dynamicDataSourceConfigMapper.deleteById(id);
    }

    private void validateDataSourceConfigExists(Long id) {
        if (dynamicDataSourceConfigMapper.selectById(id) == null) {
            throw exception(DATASOURCE_CONFIG_NOT_EXISTS);
        }
    }

    private void validateNameUnique(Long id, String name) {
        if (StrUtil.isBlank(name)) {
            return;
        }
        DynamicDataSourceConfigDO config = dynamicDataSourceConfigMapper.selectByName(name);
        if (config == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的数据源配置
        if (id == null) {
            throw exception(DATASOURCE_CONFIG_NAME_DUPLICATE);
        }
        if (!config.getId().equals(id)) {
            throw exception(DATASOURCE_CONFIG_NAME_DUPLICATE);
        }
    }

    private void validateDatabaseType(String type) {
        if (!DataSourceTypeEnum.isSupported(type)) {
            throw exception(DATASOURCE_CONFIG_INVALID_TYPE);
        }
    }

    @Override
    public DynamicDataSourceConfigDO getDataSourceConfig(Long id) {
        return dynamicDataSourceConfigMapper.selectById(id);
    }

    @Override
    public PageResult<DynamicDataSourceConfigDO> getDataSourceConfigPage(DataSourceConfigPageReqVO pageReqVO) {
        return dynamicDataSourceConfigMapper.selectPage(pageReqVO);
    }

    @Override
    public List<DynamicDataSourceConfigDO> getDataSourceConfigList(DataSourceConfigPageReqVO exportReqVO) {
        return dynamicDataSourceConfigMapper.selectList(exportReqVO);
    }

    @Override
    public List<DynamicDataSourceConfigDO> getEnabledDataSourceConfigList() {
        return dynamicDataSourceConfigMapper.selectListByEnabled(true);
    }

    @Override
    public boolean testConnection(Long id) {
        DynamicDataSourceConfigDO config = getDataSourceConfig(id);
        if (config == null) {
            return false;
        }
        return testConnection(config);
    }

    @Override
    public boolean testConnection(DynamicDataSourceConfigDO config) {
        try {
            String driverClassName = DataSourceTypeEnum.getDriverClassName(config.getType());
            Class.forName(driverClassName); // 加载驱动
            
            try (Connection connection = DriverManager.getConnection(
                    config.getUrl(), config.getUsername(), config.getPassword())) {
                return connection != null && !connection.isClosed();
            }
        } catch (Exception e) {
            log.warn("数据源连接测试失败: {} - {}", config.getName(), e.getMessage());
            return false;
        }
    }

}